﻿
/// <reference path="_namespace.js" />
/// <reference path="../components/Component.js" />
/// <reference path="../ICloneable.js" />
/// <reference path="../drawing/Colour.js" />
/// <reference path="../drawing/Face.js" />
/// <reference path="../math/Vector3.js" />
/// <reference path="Math.js" />
/// <reference path="../math/Ray3.js" />
/// <reference path="/ajax/events/EventRouter.js"/>

/*****************************************************************************
Ajax library class definition. Kevin Robinson 2006.
Class: ajax.drawing.Timer
/*****************************************************************************/

ajax.lang.addNamespace("ajax.geometry");

// We require *high speed* access to the following classes so load them now...

ajax.lang.imports("ajax.drawing.Colour");
ajax.lang.imports("ajax.drawing.Face");
ajax.lang.imports("ajax.math.Vector3");
ajax.lang.imports("ajax.math.Matrix");
ajax.lang.imports("ajax.geometry.Math");
ajax.lang.imports("ajax.math.Ray3");
ajax.lang.imports("ajax.geometry.MeshCollisionEventArgs");

//-----------------------------------------------------------------------------
ajax.geometry.Mesh = function(doc)
{
    ajax.geometry.Mesh.superConstructor.call(this, doc);
    
    // Public Properties
    this.vertexBuffer = new Array(); // will be Array of CVector3
    this.indexBuffer = new Array(); // Will be Array of shorts    
    this.faces = new Array(); // Will be Array of CFace
    this.materialColors = new Array(); // Will be Array of Colours 
    this.radius = 0;           // will store object Radius; 
    this._basis = new ajax.math.Vector3(1, 1, 1);   
    
    this.getEvent(this.getWindow(), "onNearCallback");
    this.getEvent(this.getWindow(), "onCollisionCallback");    
}

//-----------------------------------------------------------------------------
ajax.lang.extend("ajax.geometry.Mesh", "ajax.components.Component"); // Inherit
ajax.lang.supports("ajax.geometry.Mesh", "ajax.ICloneable");
if ($getDesignMode())
    ajax.geometry.Mesh.prototype = ajax.components.Component.prototype;
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Public properties For ms Ajax   
ajax.geometry.Mesh.prototype.vertexBuffer = Array.prototype;
ajax.geometry.Mesh.prototype.indexBuffer  = Array.prototype;
ajax.geometry.Mesh.prototype.faces        = Array.prototype;
ajax.geometry.Mesh.prototype.materialColors = Array.prototype;
ajax.geometry.Mesh.prototype.radius       = 0; 
ajax.geometry.Mesh.prototype.zBias        = 0;
ajax.geometry.Mesh.prototype._basis       = ajax.math.Vector3.prototype;

// Public events
ajax.geometry.Mesh.prototype.add_nearCallback = function(handler) { this.getEvent(this.getWindow(), "onNearCallback").addListener(handler); }
ajax.geometry.Mesh.prototype.remove_nearCallback = function(handler) { this.getEvent(this.getWindow(), "onNearCallback").removeListener(handler); }
ajax.geometry.Mesh.prototype.add_collisionCallback = function(handler) { this.getEvent(this.getWindow(), "onCollisionCallback").addListener(handler); }
ajax.geometry.Mesh.prototype.remove_collisionCallback = function(handler) { this.getEvent(this.getWindow(), "onColisionCallback").removeListener(handler); }


//-----------------------------------------------------------------------------
// Public methods
ajax.geometry.Mesh.prototype.flipNormals = function()
{
    for (var i = 0; i < this.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(this.faces[i]);
        face.flipNormals();
    }           
}
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.computePlaneDistances = function(mesh)
{
    // Call the computePlaneDistance on every face we have been sent
    for (var i = 0; i < mesh.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(mesh.faces[i]);
        face.computePlaneDistance();
    }
}
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.computePlaneDistances = function()
{
    ajax.geometry.Mesh.computePlaneDistances(this);
}
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.computeSides = function(mesh)
{
    for (var i = 0; i < mesh.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(mesh.faces[i]);
        face.computeSides();
    }
}
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.computeSides = function()
{
    ajax.geometry.Mesh.computeSides(this);    
}

    
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.getBasis = function()
{///<returns type="ajax.math.Vector3"/>
    return this._basis;
}
//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.setBasis = function(basis)
{
    // Store Basis
    this._basis = basis;
    
    // loop through our verts and divide each one by the basis
    for (var i = 0; i < this.vertexBuffer.length; i++)
    {
        var vPos = ajax.math.Vector3.cast(this.vertexBuffer[i]);
        vPos.divide(this._basis);
    }
    
    // Compute (new) bounding sphere
    this.computeBoundingSphere();
    
    // Compute (new) normals
    this.computeNormals();
    
    // Compute (new) sides
    this.computeSides();
    
    // Compute (new) plane distances
    this.computePlaneDistances();
}



//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.draw = function(device)
{
    // Call the DrawFaces Method of the device, passing our faces, VertexBuffer and Radius
	device.drawFaces(this.faces, this.vertexBuffer, this.radius, this.zBias);
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.loadFromFile = function(filename)
{
	// TODO Load from file
	var loader = new ajax.net.ContentLoader(this, null, null, filename, "GET", null, false);
	var req = loader.sendRequest();	
	var doc = ajax.lang.loadXmlDoc(req.responseText);	

	// Load in the Vertices
	var vertNodes = doc.getElementsByTagName("Vertex");									
	var vb = new Array(vertNodes.length);
	for (var i = 0; i < vertNodes.length;i++)
	{
		var x=0;
		var y=0;
		var z=0;
		var child = vertNodes[i].firstChild;
		while (child)
		{
		    switch (child.nodeName)
			{
				case "X":
					x = parseFloat(ajax.lang.getXmlInnerText(child));
					break;
				case "Y":
					y = parseFloat(ajax.lang.getXmlInnerText(child));
					break;
				case "Z":
					z = parseFloat(ajax.lang.getXmlInnerText(child));
					break;
				default:
					break;
			}
			if (child.nextSibling)
			    child = child.nextSibling;
			else
			    child = 0;
		}								
		vb[i] = new ajax.math.Vector3(x, y, z);					
	}
	// Assign Vertex Buffer
	this.vertexBuffer = vb;

	// Load in the indexed Materials
	var matNodes = doc.getElementsByTagName("Material");
	this.materialColors = new Array(matNodes.length);	
	
	for (var i = 0; i < matNodes.length; i++)
	{
		var a=1;
		var r=0;
		var g=0;
		var b=0;
		var child = matNodes[i].firstChild;
		while (child)
		{
			switch (child.nodeName)
			{
				case "A":
					a = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "R":
					r = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "G":
					g = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "B":
					b = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				default:
					break;
			}
			if (child.nextSibling)
			    child = child.nextSibling;
			else
			    child = 0;
		}		
		var colour = new ajax.drawing.Colour(r, g, b, a);
		this.materialColors[i] = colour;		
	}

	// Load in the faces - remember to do the Index buffer as well
	var faceNodes = doc.getElementsByTagName("Face");
	var ib			= new Array(faceNodes.length * 3);
	var faces		= new Array(faceNodes.length);

	for (var i = 0; i < faceNodes.length;i++)
	{
		var vertA=0;
		var vertB=0;
		var vertC=0;
		var matIndex=0;
		var child = faceNodes[i].firstChild;
		while (child)
		{
			switch (child.nodeName)
			{
				case "MaterialIndex":
					matIndex = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "VertA":
					vertA = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "VertB":
					vertB = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				case "VertC":
					vertC = parseInt(ajax.lang.getXmlInnerText(child));
					break;
				default:
					break;
			}
			if (child.nextSibling)
			    child = child.nextSibling;
			else
			    child = 0;
		}
		ib[(i*3)+0] = vertA;
		ib[(i*3)+1] = vertB;
		ib[(i*3)+2] = vertC;
		faces[i] = new ajax.drawing.Face(this, vertA, vertB, vertC, matIndex);
	}
	// Assign Index buffer, Faces
	this.indexBuffer	= ib;
	this.faces			= faces;
    // Compute Normals
	this.computeNormals();
	
	this.computeBoundingSphere();
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.setColour = function(colour)
{
    for (var i = 0; i < this.materialColors.length; i++)
        this.materialColors[i] = colour;                
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.computeBoundingSphere = function()
{
	this.radius = ajax.geometry.Math.computeBoundingSphere(this.vertexBuffer); 
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.lineIntersectsBoundingSphere = function(lineVector1, lineVector2)
{///<returns type="boolean">Returns true if the line (from verts passed) intersects with this mesh's bounding sphere</summary>
    var intersections = ajax.geometry.Math.lineIntersectsSphere(lineVector1, lineVector2, new ajax.math.Vector3(), this.radius);
    if (intersections.length > 0)
        return true;
    return false;
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.computeNormals = function()
{		
    for (var i = 0; i < this.faces.length; i++)
        this.faces[i].computeFaceNormal(this.vertexBuffer);        
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.transformVertexBuffer = function(matrix)
{///<summary>Transforms all vertices in the vertex buffer by the matrix. 
    /// This is done is model space so is a permanent translation. Use this method to re-orient your mesh at startup. Normals, sides and radius are all recomputed.</summary>
    ///<param name="matrix" type="ajax.math.Matrix"/>
    this.vertexBuffer = matrix.transformVertices(this.vertexBuffer);
    this.computeNormals();
    this.computeSides();
    this.computePlaneDistances();
    this.computeBoundingSphere();
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.prototype.toString = function()
{
    return "ajax.geometry.Mesh";    
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.cast = function(obj)
{///<returns type="ajax.geometry.Mesh"/>
    return obj;    
}



 

// Collision Detection routines against a mesh
//-----------------------------------------------------------------------------
// IS THIS EVEN USED??? YES it is used to get ground height (simple style).
ajax.geometry.Mesh.lineIntersection = function(mesh, vPlayerNew, vPlayerOld, matMesh, invMatMesh)
{///<returns type="ajax.math.Vector3">Returns the hitpoint of any intersection, or null</returns>
    
    // All we do here is to find the first hitpoint for the line passed in and return it.
    var vNew = ajax.math.Vector3.createFrom(vPlayerNew);
    var vOld = ajax.math.Vector3.createFrom(vPlayerOld);
    
    // Radius is always effectively 1 as we do this from the Basis (which provides scaling)
    var fRadius = 1;
    var vHitPosition = null;
    
    // Translate our 2 player vectors by the inverse matrix to get to R3 space
    vNew.transformCoordinate(invMatMesh);
    vOld.transformCoordinate(invMatMesh);
    
    // Translate our 2 player vectors to get to e-space (by dividing by the Basis)
    vNew.divide(mesh.getBasis());
    vOld.divide(mesh.getBasis());
    
    // BIG Early out - now we are in local space for this mesh, if the line does not now
    // intersect the bounding sphere for this mesh, then we return false
    var intersections = mesh.lineIntersectsBoundingSphere(vNew, vOld);
    if (!intersections)
        return vHitPosition;
        
    // Loop through all faces
    for (var i = 0; i < mesh.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(mesh.faces[i]);
        
        // At this point, we check to see if the face is pointing AWAY (from where the player WAS).
        var fAngle = face.vectorAngle(vOld);        
        if (fAngle >= 90.0 && fAngle <= 270.0)
        {
            // It IS facing us so we must decide if they crossed the plane or are just too close...
            var bCrossedPlane = false;
            
            // To decide if they crossed the plane or not, we look at the Sign of the distance from 
            // the New and Old positions to the face
            var fDistanceNew = face.vectorDistance(vNew);
            var fDistanceOld = face.vectorDistance(vOld);
            
            // Sign is not quite accurate enough!
            if ( (fDistanceNew >= 0 && fDistanceOld <= 0) || (fDistanceNew <= 0 && fDistanceOld >= 0) )
                bCrossedPlane = true;
            
            // If the distance to the face poly is too small, or if they crossed the plane
            // then we need to find out if they are inside the poly.
            if (Math.abs(fDistanceNew) <= fRadius || bCrossedPlane)
            {
                // We need to create a Ray, so create a new vector to describe the player movement
                var vecPlayerMove = ajax.math.Vector3.subtract(vNew, vOld);
                
                // Ok Bad karma man, we're too close to this plane - if we WENT THROUGH IT
                // get the actual hit position
                if (bCrossedPlane)
                {
                    // Get the percentage along the player's ray from the OLD player position to the plane
                    var playerRay           = new ajax.math.Ray3(vOld, vecPlayerMove);
                    var fDistanceToPlane    = playerRay.distanceToPlaneFromFace(face);
                    
                    // Protected divide by zero
                    var fMoveLength = vecPlayerMove.length();
                    var fPercent     = 0;
                    
                    if (fMoveLength != 0)
                        fPercent = fDistanceToPlane / fMoveLength;
                    else    
                        fPercent = 0.000001;
                    
                    // Get the actually HIT position
                    vHitPosition = ajax.math.Vector3.add(vNew, ajax.math.Vector3.multiplyScalar(face.normal, fPercent));                                                                               
                }
                else
                {
                    // Get the closest point to the player on the triangle, THAT will be our hitpoint       
                    vHitPosition = ajax.math.Vector3.add(vNew, ajax.math.Vector3.multiplyScalar(face.normal, fDistanceNew));                                 
                }                
                
                // We have the hitpoint (on the plane). is it actually within our polygon?
                if (face.pointInPoly(vHitPosition))
                    break; // FOUND IT!!
                else    
                    vHitPosition = null; // discard result
                    
            }// End Too close                                        
        } // End facing towards
    } // End loop all faces
    
    if (vHitPosition != null)
    {
        // Translate Back to World Coords
        vHitPosition = ajax.math.Vector3.cast(vHitPosition);
        var basis = mesh.getBasis();        
        vHitPosition.multiply(basis);
        vHitPosition.transformCoordinate(matMesh);
    }    
    
    return vHitPosition;    
}

ajax.geometry.Mesh.prototype.lineIntersection = function(vPlayerNew, vPlayerOld, matMesh, invMatMesh)
{
    return ajax.geometry.Mesh.lineIntersection(this, vPlayerNew, vPlayerOld, matMesh, invMatMesh);
}

ajax.geometry.Mesh.prototype.computeCallbackCollisions = function(objectToAffect, invParentTransform, objectTransform)
{///<summary>Tests the objectToAffect's movement against this collision mesh. Each hit will raise our onNearCallback (in e-space) and onCollisionCallback (world space) events that clients can listen for and respond to (providing collision response).</summary>
 ///<param name="objectToAffect" type="ajax.xna.MoveController">The MoveController Node to affect</param>
 ///<param name="invParentTransform" type="ajax.math.Matrix" mayBeNull="true">The Pre-calculated INVERSE of the objectToAffect's Parent WorldMatrix. Can be null if the objectToAffect does not have a parent.</param>
 ///<param name="objectTransform" type="ajax.xna.MoveController">The MoveController this collision mesh is attached to.</param>
    // Do some casting
    objectToAffect      = ajax.xna.MoveController.cast(objectToAffect);
    invParentTransform  = ajax.math.Matrix.cast(invParentTransform); 
    objectTransform     = ajax.xna.Moveable.cast(objectTransform);
    
    // Copy the needed Vectors and World Matrix to ensure we leave the originals intact
    var vertPlayer          = objectToAffect.getWorldMatrix().getPositionVector();
    var vertPlayerOld       = objectToAffect.getOldWorldPositionVector();
    var matWorldTransform   = objectTransform.getWorldMatrix();
    
    var objIsParent = (objectTransform == objectToAffect.getParent());
    
    var bRet = false;
    
    // The Radius is always effectively 1 because we do this based on the basis (which provides the scaling)
    var fRadius = 1;
    var vHitPosition = null;
    
    // Create a matrix to describe the inverse of the mesh object's movement
    var matInverse = ajax.math.Matrix.invert(matWorldTransform);
    
    // Translate our 2 player vectors by this matrix to get to OBJECT R3 SPACE
    vertPlayer.transformCoordinate(matInverse);
    vertPlayerOld.transformCoordinate(matInverse);
    
    // Translate our 2 player vectors to get to eSpace (by dividing by the basis)
    vertPlayer.divide(this.getBasis());
    vertPlayerOld.divide(this.getBasis());
    
    // Big Early out...now we are in local space for this mesh, if the line does not now 
    // intersect the bounding sphere for this mesh, then we return false
    var intersections = this.lineIntersectsBoundingSphere(vertPlayer, vertPlayerOld);
    if (!intersections)
        return false;
    
    // Loop through all faces
    for (var i = 0; i < this.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(this.faces[i]);
        
        // At this point, We see if we see if the face is facing away (from where the player WAS)
        var fAngle = face.vectorAngle(vertPlayerOld);
        
        // Only if its facing us...
        if (fAngle >= 90 && fAngle <= 270)
        {                        
            // It IS facing us so we must decide if they crossed the plane or are just too close
            var bCrossedPlane = false;
            
            // To decide if they crossed the pane or not, we look at the Sign of the distance from
            // the NEW & OLD positions to the face
            var fDistanceNew = face.vectorDistance(vertPlayer);
            var fDistanceOld = face.vectorDistance(vertPlayerOld);
            
            // It WAS facing us so test if we went through the plane (High speed movement)
            if ( (fDistanceNew >= 0 && fDistanceOld <=0) || (fDistanceNew <= 0 && fDistanceOld >= 0) )
                bCrossedPlane = true;
            
            // If the new distance to the Face Poly is too small or if the crossed the 
            // plane then we need find out if they are inside the poly
            if (Math.abs(fDistanceNew) <= fRadius || bCrossedPlane)
            {                                                
                // We need to create a ray so create a vector to describe the player movement
                var vecPlayerMove = ajax.math.Vector3.subtract(vertPlayer, vertPlayerOld);
                
                // OK Bad Karma man, we're too close to this plane - if we went THOUGH it
                // get the actual hit position
                if (bCrossedPlane)
                {                              
                              
                    // Get the precentage along the player's ray from the OLD player position to the plane
                    var playerRay           = new ajax.math.Ray3(vertPlayerOld, vecPlayerMove);
                    var fDistanceToPlane    = playerRay.distanceToPlaneFromFace(face);
                    
                    // Protected divide by zero
                    var fMoveLength         = vecPlayerMove.length();
                    if (fMoveLength > 3) // TODO: Remove this.
                        ajax.debug.Output.writeLn("Unusual movelength value of " + fMoveLength);
                        
                    var fPercent            = 0;
                    if (fMoveLength != 0)
                        fPercent = fDistanceToPlane / fMoveLength;
                    else
                        fPercent = 0.000001;
                    
                    // Get the ACTUAL hit position
                    vHitPosition = ajax.math.Vector3.add(vertPlayerOld, ajax.math.Vector3.multiplyScalar(vecPlayerMove, fPercent));    
                }
                else
                {                    
                    // Get the closest point to the player on the triangle, THAT will be our hitpoint
                    vHitPosition = ajax.math.Vector3.add(vertPlayer, ajax.math.Vector3.multiplyScalar(face.normal, fDistanceNew));                    
                }
                
                // Now we have to figure out if our hit point is INSIDE the trangle of our Face
                if (face.pointInPoly(vHitPosition))
                {
                    bRet = true;
                    
                    // Create a collision event, passing HANDLES to all our local vars, except radius (thus can be updated)
                    var eventLocal = new ajax.geometry.MeshCollisionEventArgs(null, this, objectToAffect, fRadius, vertPlayer,
                        vertPlayerOld, vHitPosition, face.normal, face, this, objectTransform, invParentTransform, matInverse);
                    this.onNearCallback.call(this, eventLocal);
                    
                    // Grab a copy of the vars sent back so we can send them back to World coords
                    var vNew    = ajax.math.Vector3.createFrom(eventLocal.getNewPosition());
                    var vOld    = ajax.math.Vector3.createFrom(eventLocal.getOldPosition());
                    var vHit    = ajax.math.Vector3.createFrom(eventLocal.getHitPosition());
                    var vNormal = ajax.math.Vector3.createFrom(eventLocal.getNormal());
                    
                    // Translate from eSpace to R3
                    vNew.multiply(this.getBasis());
                    vOld.multiply(this.getBasis());
                    vHit.multiply(this.getBasis());
                    
                    // Translate from R3 back to World Coords
                    vNew.transformCoordinate(matWorldTransform);
                    vOld.transformCoordinate(matWorldTransform);
                    vHit.transformCoordinate(matWorldTransform);
                    vNormal.transformNormal(matWorldTransform);
                    
                    // Now back in World Space, we've made a copy and transformed all
                    // coords we need (position only) into the object space of the parent of the object to affect.
                    // This leaves us coords that are an offset from the Object To Affect's Parent
                    var vNewLocal;
                    if (invParentTransform)
                        vNewLocal = ajax.math.Vector3.transformCoordinate(vNew, invParentTransform);
                    else
                        vNewLocal = vNew;
                    
                    // now we apply this to the Local Matrix of our object to affect
                    objectToAffect.getLocalMatrix().setPositionVector(vNewLocal);
                    
                                        
                    // Finally, now it's all done, create a new event with our WORLD vars and
                    // fire our onCollisionCallback Event.
                    var eventWorld = new ajax.geometry.MeshCollisionEventArgs(null, this, objectToAffect, fRadius, 
                        vNew, vOld, vHit, vNormal, face, this, objectTransform, invParentTransform, matInverse);
                    this.onCollisionCallback.call(this, eventWorld);
                    
                    // Since the object may have children (that depend on his position) 
                    // we must call Update Matrices on our affected object to update him AND his children.
                    objectToAffect.updateMatrices();                                      
                    
                } // hitpoint in poly.
            }// End Too close
            
            
        }// End facing us
    }// End All faces
    return bRet;
}



/*

public boolean ComputeCallbackCollisions(CMoveController objectToAffect, CMatrix invParentTransform, CMoveController objectTransform, ICollisionCallback delegate)
{
    return ComputeCallbackCollisions(this, objectToAffect, invParentTransform, objectTransform, delegate);
}

public static boolean ComputeCallbackCollisions(CMesh mesh, CMoveController objectToAffect, CMatrix invParentTransform, CMoveController objectTransform, ICollisionCallback delegate)
{                
    // Copy the needed Vectors and World Matrix to ensure we leave tthe originals intact
    // For now at least - if we collide we will update the vars then
    CVector3 vertPlayer         = null;
    CVector3 vertPlayerOld      = null;
    CMatrix matWorldTransform   = null;
    boolean objIsParent = (objectTransform == objectToAffect.getParent());
    //objIsParent = false;
    
    vertPlayer                      = new CVector3(objectToAffect.getWorldMatrix().getPositionVector());
    vertPlayerOld                   = new CVector3(objectToAffect.getOldWorldPositionVector());
    matWorldTransform               = new CMatrix(objectTransform.getWorldMatrix());
    

    boolean bRet = false;

    // The Radius is always effectively 1 because we do this based on the basis (which provides the scaling)
    float fRadius = 1;
    CVector3 vHitPosition = new CVector3();


    // Create a matrix to describe the inverse of the mesh objects movement
    CMatrix matInverse = CMatrix.Invert(matWorldTransform);

    // Translate our 2 player vectors by this matrix to get to OBJECT R3 space
    vertPlayer.TransformCoordinate(matInverse);
    vertPlayerOld.TransformCoordinate(matInverse);

    // Translate our 2 player vectors to get to eSpace (by dividing by the basis)
    vertPlayer.Divide(mesh.getBasis());
    vertPlayerOld.Divide(mesh.getBasis());

    // Big Early out...now we are in local space for this mesh, if the line does not now 
    // intersect the bounding sphere for this mesh, then we return false
    boolean intersections = mesh.LineIntersectsBoundingSphere(vertPlayer, vertPlayerOld);
    if (!intersections)
        return false;
    

    // Loop through all triangles
    for (int i = 0; i < mesh.getFaces().length; i++)
    {
        CFace face = mesh.getFaces()[i];

        // At this point, We see if we see if the face is facing away (from where the player WAS)
        float fAngle = face.VectorAngle(vertPlayerOld);

        // Only if its facing us...
        if (fAngle >= 90.0f && fAngle <= 270.0f)
        {
            // It IS facing us so we must decide if they crossed the plane or are just too close
            boolean bCrossedPlane = false;

            // To decide if they crossed the pane or not, we look at the Sign of the distance from
            // the NEW & OLD positions to the face
            float fDistanceNew = face.VectorDistance(vertPlayer);
            float fDistanceOld = face.VectorDistance(vertPlayerOld);                      

            // It WAS facing us so test if we went through the plane (High speed movement)
            if ( (fDistanceNew >= 0 && fDistanceOld <= 0) || (fDistanceNew <= 0 && fDistanceOld >= 0) )
                bCrossedPlane = true;

            // If the new distance to the Face Poly is too small or if the crossed the 
            // plane then we need find out if they are inside the poly
            if (Math.abs(fDistanceNew) <= fRadius || bCrossedPlane)
            {
                // We need to create a ray so create a vector to describe the player movement
                CVector3 vecPlayerMove = CVector3.Subtract(vertPlayer, vertPlayerOld);

                // OK Bad Karma man, we'return too close to this plane - if we went THOUGH it
                // get the actual hit position
                if (bCrossedPlane)
                {
                    // Get the precentage along the player's ray from the OLD player position to the plane
                    CRay3 playerRay         = new CRay3(vertPlayerOld, vecPlayerMove);
                    float fDistanceToPlane  = playerRay.DistanceToPlane(face);

                    // Protected divide by zero
                    float fMoveLength = vecPlayerMove.Length();
                    float fPercent = 0;
                    if (fMoveLength != 0)
                        fPercent = fDistanceToPlane / fMoveLength;
                    else
                        fPercent = Float.MIN_VALUE;

                    // Get the actualy hit position
                    vHitPosition = CVector3.Add(vertPlayerOld, CVector3.Multiply(vecPlayerMove, fPercent));
                }
                else
                {
                    // Get the closest point to the player on the triangle, THAT will be our hitpoint
                    vHitPosition = CVector3.Add(vertPlayer, CVector3.Multiply(face.normal, fDistanceNew));
                }

                // Now we have to figure out if our hit point is INSIDE the trangle of our Face
                if (face.PointInPoly(vHitPosition))
                {
                    // update our player position to get him away from the wall for the sake of our next loop
                    // So the new pos = HitPos + (Normal(1 unit) * Radius)
                    //vertPlayer = CVector3.Add(vHitPosition, CVector3.Multiply(face.normal, fRadius));

                    // Create a collision event, passing HANDLES to all our local vars, except radius (thus can be updated)
                    CollisionEvent eventLocal = new CollisionEvent(fRadius, vertPlayer, vertPlayerOld, vHitPosition, face.normal, face, mesh, objectTransform, invParentTransform, matInverse);
                    delegate.onNearCallback(objectToAffect, eventLocal);

                    // Grab a copy of the vars sent back so we can send them back to World coords
                    CVector3 vNew       = new CVector3(eventLocal.NewPos);
                    CVector3 vOld       = new CVector3(eventLocal.OldPos);
                    CVector3 vHit       = new CVector3(eventLocal.HitPos);
                    CVector3 vNormal    = new CVector3(eventLocal.Normal);

                    // Translate from eSpace to R3
                    vNew.Multiply(mesh.getBasis());
                    vOld.Multiply(mesh.getBasis());
                    vHit.Multiply(mesh.getBasis());
                    
                    // Translate from R3 space back into World Coords
                    vNew.TransformCoordinate(matWorldTransform);
                    vOld.TransformCoordinate(matWorldTransform);
                    vHit.TransformCoordinate(matWorldTransform);
                    vNormal.TransformNormal(matWorldTransform);
                    
                    
                    // We are back into world space now. We must make a copy and transform all
                    // co-ords we need (just position) into the object space of the parent of the object to affect,
                    // thus leaving us coords that are an offset from the OTAffect's parent                        
                    CVector3 vNewLocal  = CVector3.TransformCoordinate(vNew, invParentTransform);                                                                        
                    
                    // Now we apply to the local of our object to affect
                    objectToAffect.getLocalMatrix().setPositionVector(vNewLocal);                                                

                    // Since the object may have children (that depend on his position)
                    // Call UpdateMatrices on our affected object to update him AND his children.
                    objectToAffect.UpdateMatrices();
                  
                    // Finally, now its all done, create a new event with WORLD vars and
                    // call the collision callback on the delegate so the client can respond.
                    CollisionEvent eventWorld = new CollisionEvent(fRadius, vNew, vOld, vHit, vNormal, face, mesh, objectTransform, invParentTransform, matInverse);
                    delegate.onCollisionCallback(objectToAffect, eventWorld);  
                }

            }                
        }            
    }                    
    return bRet;
}

public CVector3 LineIntersection(CVector3 vPlayerNew, CVector3 vPlayerOld, CMatrix matMesh, CMatrix invMatMesh)
{
    return LineIntersection(this, vPlayerNew, vPlayerOld, matMesh, invMatMesh);
}
public static CVector3 LineIntersection(CMesh mesh, CVector3 vPlayerNew, CVector3 vPlayerOld, CMatrix matMesh, CMatrix invMatMesh)
{                
    // All we do here is to find the first hitpoint for the line passed in and return it.
    CVector3 vNew = new CVector3(vPlayerNew);
    CVector3 vOld = new CVector3(vPlayerOld);
    // The Radius is always effectively 1 because we do this based on the basis (which provides the scaling)
    float fRadius = 1;
    CVector3 vHitPosition = null;

    // Translate our 2 player vectors by this matrix to get to OBJECT R3 space
    vNew.TransformCoordinate(invMatMesh);
    vOld.TransformCoordinate(invMatMesh);

    // Translate our 2 player vectors to get to eSpace (by dividing by the basis)
    vNew.Divide(mesh.getBasis());
    vOld.Divide(mesh.getBasis());

    // Big Early out...now we are in local space for this mesh, if the line does not now 
    // intersect the bounding sphere for this mesh, then we return false
    boolean intersections = mesh.LineIntersectsBoundingSphere(vNew, vOld);
    if (!intersections)
        return vHitPosition;
    

    // Loop through all triangles
    for (int i = 0; i < mesh.getFaces().length; i++)
    {
        CFace face = mesh.getFaces()[i];

        // At this point, We see if we see if the face is facing away (from where the player WAS)
        float fAngle = face.VectorAngle(vOld);

        // Only if its facing us...
        if (fAngle >= 90.0f && fAngle <= 270.0f)
        {
            // It IS facing us so we must decide if they crossed the plane or are just too close
            boolean bCrossedPlane = false;

            // To decide if they crossed the pane or not, we look at the Sign of the distance from
            // the NEW & OLD positions to the face
            float fDistanceNew = face.VectorDistance(vNew);
            float fDistanceOld = face.VectorDistance(vOld);                      

            // It WAS facing us so test if we went through the plane (High speed movement)
            if ( (fDistanceNew >= 0 && fDistanceOld <= 0) || (fDistanceNew <= 0 && fDistanceOld >= 0) )
                bCrossedPlane = true;

            // If the new distance to the Face Poly is too small or if the crossed the 
            // plane then we need find out if they are inside the poly
            if (Math.abs(fDistanceNew) <= fRadius || bCrossedPlane)
            {
                // We need to create a ray so create a vector to describe the player movement
                CVector3 vecPlayerMove = CVector3.Subtract(vNew, vOld);

                // OK Bad Karma man, we'return too close to this plane - if we went THOUGH it
                // get the actual hit position
                if (bCrossedPlane)
                {
                    // Get the precentage along the player's ray from the OLD player position to the plane
                    CRay3 playerRay         = new CRay3(vOld, vecPlayerMove);
                    float fDistanceToPlane  = playerRay.DistanceToPlane(face);

                    // Protected divide by zero
                    float fMoveLength = vecPlayerMove.Length();
                    float fPercent = 0;
                    if (fMoveLength != 0)
                        fPercent = fDistanceToPlane / fMoveLength;
                    else
                        fPercent = Float.MIN_VALUE;

                    // Get the actualy hit position
                    vHitPosition = CVector3.Add(vOld, CVector3.Multiply(vecPlayerMove, fPercent));
                    
                }
                else
                {
                    // Get the closest point to the player on the triangle, THAT will be our hitpoint
                    vHitPosition = CVector3.Add(vNew, CVector3.Multiply(face.normal, fDistanceNew));
                    
                }
                if (face.PointInPoly(vHitPosition))
                {
                    if (face.normal.y > .4f)
                        break; // Found it!
                    else 
                        vHitPosition = null;
                }
                    
                else
                    vHitPosition = null;                    
            }
        }
    }
    if (vHitPosition != null)
    {
        CVector3 basis = mesh.getBasis();
        // Translate back to world coords
        vHitPosition.Multiply(basis);
        vHitPosition.TransformCoordinate(matMesh);
    }
    return vHitPosition;
 }
*/     

//-----------------------------------------------------------------------------
//Statics - Types of Mesh creation

ajax.geometry.Mesh.clone = function(mesh)
{///<param name="mesh" type="ajax.geometry.Mesh">The Mesh to copy.</param>
 ///<returns type="ajax.geometry.Mesh">Returns a deep copy of the supplied mesh.</returns>

    var ret = new ajax.geometry.Mesh(mesh.getDocument());
    // Copy verts
    ret.vertexBuffer = new Array(mesh.vertexBuffer.length);
    for (var i = 0; i < ret.vertexBuffer.length; i++)
        ret.vertexBuffer[i] = ajax.math.Vector3.createFrom(mesh.vertexBuffer[i]);

    // Copy indices
    ret.indexBuffer = new Array(mesh.indexBuffer.length);
    for (var i = 0; i < ret.indexBuffer.length; i++)
        ret.indexBuffer[i] = mesh.indexBuffer[i];

    // Copy materials
    ret.materialColors = new Array(mesh.materialColors.length);
    for (var i = 0; i < ret.materialColors.length; i++)
    {
        var color = ajax.drawing.Colour.cast(mesh.materialColors[i]);
        ret.materialColors[i] = new ajax.drawing.Colour(color.r, color.g, color.b, color.a);
    }

    // Create faces
    ret.faces = new Array(mesh.faces.length);
    for (var i = 0; i < ret.faces.length; i++)
    {
        var face = ajax.drawing.Face.cast(mesh.faces[i]);
        ret.faces[i] = new ajax.drawing.Face(ret, face.v1, face.v2, face.v3, face.getColorIndex());
    }
        
    ret.computeNormals();
    ret.computeBoundingSphere();
    ret.computePlaneDistances();
    ret.computeSides();    
    return ret;
}

ajax.geometry.Mesh.prototype.clone = function()
{///<returns type="ajax.geometry.Mesh">Returns a deep copy of the mesh.</returns>
    return ajax.geometry.Mesh.clone(this);
}

//-----------------------------------------------------------------------------
// Static factor creation method (as no overloads)
ajax.geometry.Mesh.createCollisionMesh = function(mesh, basis)
{///<summary>Creates a new collision mesh with specified basis from original (rendering) mesh.</summary>
 ///<param name="mesh" type="ajax.geometry.mesh">The original (rendering) versino of the mesh</param>
 ///<param name="basis" type="ajax.math.Vector3">The basis (scaling) to be applied to the collisions on this collisino mesh</param>
 ///<returns type="ajax.geometry.Mesh"/>    
    var ret = ajax.geometry.Mesh.copy(mesh);
    ret.setBasis(basis);            
    return ret;
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.loadScaleAndColour = function(doc, filePath, x, y, z, colour)
{///<summary>Loads up the file specified, scales and colors it and returns it to you, for your shopping convienence.</summary>
///<param name="colour" optional="true">Optional. The colour for the entire mesh. By default the original multi-colours of the mesh are used.</param>
///<returns type="ajax.geometry.Mesh"/>
    
    var mesh = new ajax.geometry.Mesh(doc);
    x = ajax.isDefined(x) ? x : 1;
    y = ajax.isDefined(y) ? y : 1;
    z = ajax.isDefined(z) ? z : 1;

    mesh.loadFromFile(filePath);
    // Scale as suggested.
    mesh.transformVertexBuffer(ajax.math.Matrix.createScale(new ajax.math.Vector3(x, y, z)));
    // If a colour is supplied, apply that
    if (colour)    
        mesh.setColour(colour);
    return mesh;
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.createTieLow = function(doc, x, y, z, colour)
{///<param name="colour" optional="true">Optional. The colour for the entire mesh. By default the original multi-colours of the mesh are used.</param>
 ///<returns type="ajax.geometry.Mesh">Returns a mesh of a very low poly Tie fighter.</returns>
    return ajax.geometry.Mesh.loadScaleAndColour(doc, ajax.getDomain("/media/meshes/TieLow.xml"), x, y, z, colour);    
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.createBox = function(doc, x, y, z, color)
{///<returns type="ajax.geometry.Mesh">All parameters are optional - you will get a white unit box by default</returns>

    color   = typeof(color) == "undefined" ? new ajax.drawing.Colour(255, 255, 255) : color;
    x       = typeof(x) == "undefined" ? 1 : x;
    y       = typeof(y) == "undefined" ? 1 : y;
    z       = typeof(z) == "undefined" ? 1 : z;
    
	var result = new ajax.geometry.Mesh(doc);
	// Set result mesh color to that passed (there is only one)
    result.materialColors[0] = color;
	var x2 = x/2;
	var y2 = y/2;
	var z2 = z/2;

	// Create the vertex & index buffers
	var vb = new Array(8);
	
	// Front face
	vb[0] = new ajax.math.Vector3(-x2,  y2, -z2);//
	vb[1] = new ajax.math.Vector3(-x2, -y2, -z2);
	vb[2] = new ajax.math.Vector3( x2,  y2, -z2);//
	vb[3] = new ajax.math.Vector3( x2, -y2, -z2);
	// Back face
	vb[4] = new ajax.math.Vector3( x2,  y2,  z2);
	vb[5] = new ajax.math.Vector3( x2, -y2,  z2);
	vb[6] = new ajax.math.Vector3(-x2,  y2,  z2);//
	vb[7] = new ajax.math.Vector3(-x2, -y2,  z2);

	// Set the Vertexbuffer of our mesh
	result.vertexBuffer = vb;


	// Index Buffer
	var ib = new Array(36);
	ib[0] = 0; ib[1] = 1; ib[2] = 2;
	ib[3] = 2; ib[4] = 1; ib[5] = 3;
	ib[6] = 2; ib[7] = 3; ib[8] = 4;
	ib[9] = 4; ib[10] = 3; ib[11] = 5;
	ib[12] = 6; ib[13] = 7; ib[14] = 0;
	ib[15] = 0; ib[16] = 7; ib[17] = 1;
	ib[18] = 6; ib[19] = 0; ib[20] = 4;
	ib[21] = 4; ib[22] = 0; ib[23] = 2;
	ib[24] = 1; ib[25] = 7; ib[26] = 3;	
	ib[27] = 3; ib[28] = 7; ib[29] = 5;
	//ib[27] = 5; ib[28] = 3; ib[29] = 7; // Changed from Java version
	ib[30] = 4; ib[31] = 5; ib[32] = 6;
	ib[33] = 6; ib[34] = 5; ib[35] = 7;
	
	// Set the IndexBuffer to our mesh
	result.indexBuffer = ib;

	// Create the faces with the specified color
	var faces = new Array(ib.length/3);
	for (var i = 0; i < faces.length; i++)
	{
		faces[i] = new ajax.drawing.Face(result,
			ib[(i*3)+0], ib[(i*3)+1], ib[(i*3)+2], 0
			);
	}
	result.faces = faces;

	// Generate Normals
	result.computeNormals();
	result.computeBoundingSphere();

	//Deliver the mesh back
	return result;
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.createQuad = function(doc, x, y, color)
{///<returns type="ajax.geometry.Mesh">All parameters are optional - you will get a white unit, front facing Quad by default</returns>
    color   = typeof(color) == "undefined" ? new ajax.drawing.Colour(255, 255, 255) : color;
    x       = typeof(x) == "undefined" ? 1 : x;
    y       = typeof(y) == "undefined" ? 1 : y;
    z       = typeof(z) == "undefined" ? 1 : z;
    
    var x2 = x/2;
    var y2 = y/2;
    
    var result = new ajax.geometry.Mesh(doc);
    // Set result mesh color to that passed (there is only one)
    result.materialColors[0] = color;
    
    // Create the vertex and index buffers
    var vb = new Array(4);
    vb[0] = new ajax.math.Vector3(-x2, -y2, 0);   // Front bottom left
    vb[1] = new ajax.math.Vector3( x2, -y2, 0);   // Front bottom right
    vb[2] = new ajax.math.Vector3(-x2,  y2, 0);   // Front top left
    vb[3] = new ajax.math.Vector3( x2,  y2, 0);   // Front top right
    result.vertexBuffer = vb;
    
    // Index buffer
    var ib = new Array(6);
    ib[0] = 0; ib[1] = 1; ib[2] = 2;
    ib[3] = 3; ib[4] = 2; ib[5] = 1;
    result.indexBuffer = ib;
    
    // Create the faces, use material index 0
    var faces = new Array(ib.length/3);
    for (var i = 0; i < faces.length; i++)
    {
        faces[i] = new ajax.drawing.Face(result,
            ib[(i*3)+0], ib[(i*3)+1], ib[(i*3)+2], 0
        );
    }
    result.faces = faces;
    // Generate normals etc
    result.computeNormals();
    result.computeBoundingSphere();
    
    return result;
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.createPyramidOriginBottom = function(doc, x, y, z, color)
{///<returns type="ajax.geometry.Mesh">All parameters are optional - you will get a white unit Pyramid by default</returns>
    color   = typeof(color) == "undefined" ? new ajax.drawing.Colour(255, 255, 255) : color;
    x       = typeof(x) == "undefined" ? 1 : x;
    y       = typeof(y) == "undefined" ? 1 : y;
    z       = typeof(z) == "undefined" ? 1 : z;
   
    
    var result = new ajax.geometry.Mesh(doc);
    // Set result mesh color to that passed (there is only one)
    result.materialColors[0] = color;
    
    // Create the vertex and index buffers
    var vb = new Array(5);    
    vb[0] = new ajax.math.Vector3(x * -0.5, 0, z * -0.5); // Front left
    vb[1] = new ajax.math.Vector3(x *  0.5, 0, z * -0.5); // Front right
    vb[2] = new ajax.math.Vector3(x * -0.5, 0, x *  0.5); // Rear Left
    vb[3] = new ajax.math.Vector3(x *  0.5, 0, x *  0.5); // Rear Right;
    vb[4] = new ajax.math.Vector3(0,        y,      0  ); // Center
    result.vertexBuffer = vb;
    
    // index buffer
    var ib = new Array(18);
    ib[0] = 1; ib[1] = 4; ib[2] = 0; // front
    ib[3] = 3; ib[4] = 4; ib[5] = 1; // Right
    ib[6] = 2; ib[7] = 4; ib[8] = 3; // Rear
    ib[9] = 0; ib[10] = 4; ib[11] = 2; // Left
    ib[12] = 0; ib[13] = 3; ib[14] = 1; // bottom1
    ib[15] = 3; ib[16] = 0; ib[17] = 2; // bottom2
    result.indexBuffer = ib;
    
   // Create the faces with the specified color
	var faces = new Array(ib.length/3);
	for (var i = 0; i < faces.length; i++)
	{
		faces[i] = new ajax.drawing.Face(result,
			ib[(i*3)+0], ib[(i*3)+1], ib[(i*3)+2], 0
			);
	}
	result.faces = faces;

	// Generate Normals
	result.computeNormals();
	result.computeBoundingSphere();

	//Deliver the mesh back
	return result;
    
}

//-----------------------------------------------------------------------------
ajax.geometry.Mesh.createBoxOriginBottom = function(doc, x, y, z, color)
{///<returns type="ajax.geometry.Mesh">All parameters are optional - you will get a white unit box by default</returns>

    color   = typeof(color) == "undefined" ? new ajax.drawing.Colour(255, 255, 255) : color;
    x       = typeof(x) == "undefined" ? 1 : x;
    y       = typeof(y) == "undefined" ? 1 : y;
    z       = typeof(z) == "undefined" ? 1 : z;
    
	var result = new ajax.geometry.Mesh(doc);
    // Set result mesh color to that passed (there is only one)
    result.materialColors[0] = color;
    
	// Create the vertex & index buffers
	var vb = new Array(8);
	
	// Front face
	vb[0] = new ajax.math.Vector3( x * -0.5,  y * 1, z * -0.5);
	vb[1] = new ajax.math.Vector3( x * -0.5,  0,     z * -0.5);
	vb[2] = new ajax.math.Vector3( x *  0.5,  y * 1, z * -0.5);
	vb[3] = new ajax.math.Vector3( x *  0.5,  0,     z * -0.5);
	// Back face
	vb[4] = new ajax.math.Vector3( x *  0.5,  y * 1,  z * 0.5);
	vb[5] = new ajax.math.Vector3( x *  0.5,  0,      z * 0.5);
	vb[6] = new ajax.math.Vector3( x * -0.5,  y * 1,  z * 0.5);
	vb[7] = new ajax.math.Vector3( x * -0.5,  0,      z * 0.5);

	// Set the Vertexbuffer of our mesh
	result.vertexBuffer = vb;

	// Index Buffer
	var ib = new Array(36);
	ib[0] = 0; ib[1] = 1; ib[2] = 2;
	ib[3] = 2; ib[4] = 1; ib[5] = 3;
	ib[6] = 2; ib[7] = 3; ib[8] = 4;
	ib[9] = 4; ib[10] = 3; ib[11] = 5;
	ib[12] = 6; ib[13] = 7; ib[14] = 0;
	ib[15] = 0; ib[16] = 7; ib[17] = 1;
	ib[18] = 6; ib[19] = 0; ib[20] = 4;
	ib[21] = 4; ib[22] = 0; ib[23] = 2;
	ib[24] = 1; ib[25] = 7; ib[26] = 3;
	ib[27] = 3; ib[28] = 7; ib[29] = 5;
	ib[30] = 4; ib[31] = 5; ib[32] = 6;
	ib[33] = 6; ib[34] = 5; ib[35] = 7;
	
	// Sset the IndexBuffer to our mesh
	result.indexBuffer = ib;

	// Create the faces with the specified color
	var faces = new Array(ib.length/3);
	for (var i = 0; i < faces.length; i++)
	{
		faces[i] = new ajax.drawing.Face(result,
			ib[(i*3)+0], ib[(i*3)+1], ib[(i*3)+2], 0
			);
	}
	result.faces = faces;

	// Generate Normals
	result.computeNormals();
	result.computeBoundingSphere();

	//Deliver the mesh back
	return result;

}


